package com.xfcy.blog.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xfcy.blog.common.R;
import com.xfcy.blog.common.constant.Constant;
import com.xfcy.blog.common.constant.RedisConstant;
import com.xfcy.blog.common.constant.SqlConstant;
import com.xfcy.blog.common.enums.LoginTypeEnum;
import com.xfcy.blog.common.enums.ResultCode;
import com.xfcy.blog.common.enums.UploadEnum;
import com.xfcy.blog.common.enums.UserStatusEnum;
import com.xfcy.blog.dto.EmailLoginDTO;
import com.xfcy.blog.dto.EmailRegisterDTO;
import com.xfcy.blog.dto.UserAuthDTO;
import com.xfcy.blog.entity.User;
import com.xfcy.blog.entity.UserRole;
import com.xfcy.blog.entity.WebConfig;
import com.xfcy.blog.exception.CustomerException;
import com.xfcy.blog.mapper.UserAuthMapper;
import com.xfcy.blog.entity.UserAuth;
import com.xfcy.blog.service.*;
import com.xfcy.blog.strategy.context.FileUploadStrategyContext;
import com.xfcy.blog.utils.BCryptUtils;
import com.xfcy.blog.utils.EmailUtils;
import com.xfcy.blog.vo.UserInfoVO;
import io.swagger.util.Yaml;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import javax.mail.MessagingException;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 用户信息表(UserAuth)表服务实现类
 *
 * @author 晓风残月Lx
 * @since 2023-04-09 11:03:06
 */
@Service("userAuthService")
public class UserAuthServiceImpl extends ServiceImpl<UserAuthMapper, UserAuth> implements UserAuthService {

    @Resource
    private EmailService emailService;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private UserService userService;

    @Resource
    private WebConfigService webConfigService;

    @Resource
    private UserRoleService userRoleService;

    @Resource
    private FileUploadStrategyContext fileUploadStrategyContext;

    @Resource
    private SystemConfigService systemConfigService;

    @Resource
    private RoleService roleService;


    // Web方法

    /**
     * 通过邮箱注册
     * @param emailRegisterDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R emailRegister(EmailRegisterDTO emailRegisterDTO) {

        checkCode(RedisConstant.EMAIL_CODE + emailRegisterDTO.getEmail(), emailRegisterDTO.getCode());

        // 通过邮箱(即用户名)查询用户
        User user = getUserByEmail(emailRegisterDTO.getEmail());
        Assert.isNull(user, ResultCode.EMAIL_IS_EXIST.getDesc());

        // 获得默认头像
        WebConfig config = webConfigService.getOne(new QueryWrapper<WebConfig>().select(SqlConstant.TOURIST_AVATAR).last(SqlConstant.LIMIT_ONE));
        UserAuth userAuth = UserAuth.builder().email(emailRegisterDTO.getEmail())
                .avatar(config.getTouristAvatar())
                .nickname(emailRegisterDTO.getNickname())
                .build();
        baseMapper.insert(userAuth);

        user = User.builder().username(emailRegisterDTO.getEmail())
                .roleId(SqlConstant.USER_ROLE_ID)
                .loginType(LoginTypeEnum.EMAIL.getType())
                .userAuthId(userAuth.getId())
                .password(BCryptUtils.encrypt(emailRegisterDTO.getPassword())).build();

        boolean save = userService.save(user);

        UserRole userRole = UserRole.builder().roleId(SqlConstant.USER_ROLE_ID).
                userId(user.getId()).build();
        userRoleService.save(userRole);

        redisTemplate.delete(RedisConstant.EMAIL_CODE + emailRegisterDTO.getEmail());

        return save ? R.success("注册成功") : R.error(ResultCode.ERROR_DEFAULT.getDesc());
    }

    /**
     * 邮箱发送验证码
     * @param email
     * @return
     */
    @Override
    public R sendEmailCode(String email) {
        try {
            // 判断是否是正常的邮箱地址
            Assert.isTrue(EmailUtils.isEmailAddress(email), "请填写合法的邮箱地址！！！");
            emailService.sendCode(email);
        } catch (MessagingException e) {
            e.printStackTrace();
            return R.error(ResultCode.ERROR_DEFAULT.getDesc());
        }
        return R.success("操作成功");
    }

    /**
     * 通过邮箱和密码登录
     * @param emailLoginDTO
     * @return
     */
    @Override
    public R emailLogin(EmailLoginDTO emailLoginDTO) {

        User user = getUserByEmail(emailLoginDTO.getEmail());

        if (null == user) {
            throw new CustomerException(ResultCode.NOT_REGISTER);
        }
        Assert.isTrue(user.getStatus() == UserStatusEnum.NORMAL.getCode(), ResultCode.EMAIL_DISABLE_LOGIN.getDesc());

        boolean checkPassword = BCryptUtils.checkPassword(emailLoginDTO.getPassword(), user.getPassword());
        Assert.isTrue(checkPassword, ResultCode.ERROR_PASSWORD.getDesc());

        UserAuth userAuth = baseMapper.selectById(user.getUserAuthId());

        // 登录到自定义监听器
        StpUtil.login(user.getId().longValue());
        String tokenValue = StpUtil.getTokenValue();

        UserInfoVO userInfoVO = UserInfoVO.builder().id(user.getId()).userInfoId(userAuth.getId()).avatar(userAuth.getAvatar()).nickname(userAuth.getNickname())
                .intro(userAuth.getIntro()).email(user.getUsername()).loginType(user.getLoginType()).token(tokenValue).build();

        userInfoVO.setArticleLikeSet(getArticleLike(user.getId()));
        userInfoVO.setCommentLikeSet(getCommentLike(user.getId()));

        return R.success(userInfoVO);
    }

    /**
     * 注销
     * @return
     */
    @Override
    public R logout() {
        StpUtil.logout();
        return R.success("退出登录成功！");
    }

    /**
     * 更新用户信息
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R updateUser(UserAuthDTO userAuthDTO) {
        UserAuth userAuth = getUserAuth();
        String avatar = userAuth.getAvatar();

        // 如果是默认图片，不删除
        WebConfig config = webConfigService.getOne(new QueryWrapper<WebConfig>().select(SqlConstant.TOURIST_AVATAR).last(SqlConstant.LIMIT_ONE));

        // 判断和数据库图片是否相同，并且数据库中是否是默认头像
        if (!avatar.equals(config.getTouristAvatar()) && !userAuthDTO.getAvatar().equals(avatar)) {
            redisTemplate.opsForSet().remove(RedisConstant.DB_FILE_UPLOAD_IMAGES, avatar);
            redisTemplate.opsForSet().add(RedisConstant.DB_FILE_UPLOAD_IMAGES, userAuthDTO.getAvatar());
        }

        userAuth.setNickname(userAuthDTO.getNickname());
        userAuth.setIntro(userAuthDTO.getIntro());
        userAuth.setAvatar(userAuthDTO.getAvatar());

        boolean update = updateById(userAuth);
        return update ? R.success("修改信息成功"): R.error(ResultCode.ERROR_DEFAULT.getDesc());
    }

    /**
     * 绑定邮箱
     * @param emailLoginDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R bindEmail(EmailLoginDTO emailLoginDTO) {
        // 判断该邮箱是否使用过
        User user = getUserByEmail(emailLoginDTO.getEmail());
        Assert.isNull(user, ResultCode.EMAIL_IS_EXIST.getDesc());
        // 核实验证码
        checkCode(RedisConstant.EMAIL_CODE + emailLoginDTO.getEmail(), emailLoginDTO.getCode());

        // 更改用户信息
        UserAuth userAuth = getUserAuth();
        userAuth.setEmail(emailLoginDTO.getEmail());
        userService.update(new UpdateWrapper<User>().set(SqlConstant.USERNAME, emailLoginDTO.getEmail()).eq(SqlConstant.ID, StpUtil.getLoginIdAsInt()));

        boolean update = updateById(userAuth);
        redisTemplate.delete(RedisConstant.EMAIL_CODE + emailLoginDTO.getEmail());

        return update ? R.success("绑定邮箱成功"): R.error(ResultCode.ERROR_DEFAULT.getDesc());
    }

    /**
     * 邮箱账号修改密码
     * @param emailRegisterDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R updatePassword(EmailRegisterDTO emailRegisterDTO) {
        checkCode(RedisConstant.EMAIL_CODE + emailRegisterDTO.getEmail(), emailRegisterDTO.getCode());

        User user = getUserByEmail(emailRegisterDTO.getEmail());
        Assert.notNull(user, ResultCode.ERROR_MUST_REGISTER.getDesc());

        user.setPassword(BCryptUtils.encrypt(emailRegisterDTO.getPassword()));
        boolean update = userService.updateById(user);

        redisTemplate.delete(RedisConstant.EMAIL_CODE + emailRegisterDTO.getEmail());

        return update ? R.success("修改成功！") : R.error(ResultCode.ERROR_MUST_REGISTER.getDesc());

    }


    // ********** 自定义方法


    /**
     * 校验验证码是否相同
     * @param key
     * @param soureCode
     */
    private void checkCode(String key, String soureCode) {
        String code = redisTemplate.opsForValue().get(key).toString();
        Assert.isTrue(code.equalsIgnoreCase(soureCode), ResultCode.ERROR_EXCEPTION_MOBILE_CODE.getDesc());;
    }

    /**
     * 通过邮箱(即用户名)查询用户
     * @param email
     * @return
     */
    private User getUserByEmail(String email) {
        return userService.getOne(new QueryWrapper<User>().eq(SqlConstant.USERNAME, email));
    }

    private UserAuth getUserAuth() {
        User user = userService.getById(StpUtil.getLoginIdAsInt());
       return baseMapper.selectById(user.getUserAuthId());
    }

    /**
     * 用户点赞过的评论
     * @param userId
     * @return
     */
    private Set<Object> getCommentLike(Integer userId) {
        return redisTemplate.opsForSet().members(RedisConstant.USER_COMMENT_LIKED + userId);
    }

    private Set<Object> getArticleLike(Integer userId) {
        return redisTemplate.opsForSet().members(RedisConstant.ARTICLE_USER_LIKE + userId);
    }



}


